import re
from django.db import models

"""
This comes from djangosnippets, can't remember the snippet number
"""

class SlugNotCorrectlyPrePopulated(Exception): 
	pass 

def _string_to_slug(s):	
	raw_data = s
	# normalze string as proposed on http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/251871
	# by Aaron Bentley, 2006/01/02
	try:
		import unicodedata		
		if type(raw_data) == type(u''):
			raw_data = unicodedata.normalize('NFKD', raw_data).encode('ascii', 'ignore')
		else:
			raw_data = unicodedata.normalize('NFKD', raw_data.decode('utf-8', 'replace')).encode('ascii', 'ignore')
	except:
		pass
	return re.sub(r'[^a-z0-9-]+', '_', raw_data.lower()).strip('_')

	
# as proposed by Archatas (http://www.djangosnippets.org/users/Archatas/)
def _get_unique_value(model, proposal, field_name="slug", instance_pk=None, separator="-"):
	""" Returns unique string by the proposed one.
	Optionally takes:
	* field name which can  be 'slug', 'username', 'invoice_number', etc.
	* the primary key of the instance to which the string will be assigned.
	* separator which can be '-', '_', ' ', '', etc.
	By default, for proposal 'example' returns strings from the sequence:
		'example', 'example-2', 'example-3', 'example-4', ...
	"""
	if instance_pk:
		similar_ones = model.objects.filter(**{field_name + "__startswith": proposal}).exclude(pk=instance_pk).values(field_name)
	else:
		similar_ones = model.objects.filter(**{field_name + "__startswith": proposal}).values(field_name)
	similar_ones = [elem[field_name] for elem in similar_ones]
	if proposal not in similar_ones:
		return proposal
	else:
		numbers = []
		for value in similar_ones:
			match = re.match(r'^%s%s(\d+)$' % (proposal, separator), value)
			if match:
				numbers.append(int(match.group(1)))
		if len(numbers)==0:
			return "%s%s2" % (proposal, separator)
		else:
			largest = sorted(numbers)[-1]
			return "%s%s%d" % (proposal, separator, largest + 1)

def _get_fields_and_data(model):
	"""
	returns list of [ [ models.SlugField object, slugFieldValue ] , [ x, y] ... ]
	"""
	opts = model._meta
	slug_fields = []
	for f in opts.fields:
		if isinstance(f, models.SlugField):
			if not f.prepopulate_from:
				raise SlugNotCorrectlyPrePopulated , "Slug for %s is not prepopulated" % f.name
			prepop = []
			for n in f.prepopulate_from:
				if not hasattr(model, n):
					raise SlugNotCorrectlyPrePopulated , "Slug for %s is to be prepopulated from %s, yet %s.%s does not exist" % (f.name , n , type(model), n)
				else:
					prepop.append(getattr(model, n))
			slug_fields.append([f , "_".join(prepop)])
	return slug_fields
	
def slugify(sender, **kwargs):	
	for slugs in _get_fields_and_data(instance):	
		original_slug = _string_to_slug(slugs[1])
		slug = original_slug
		ct = 0;
		try:
			# See if object is new
			# To prevent altering urls, don't update slug on existing objects
			sender.objects.get(pk=instance._get_pk_val())
		except:
			slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator="_")
			setattr(instance, slugs[0].name, slug)



def _getSlugFieldVal(instance, slugField = None):
	"""
	returns SlugField value for current instance
	it looks up models.SlugField object in instance fields
	and reutrns its value

	"""
	fields = instance._meta
	retVal = []

	if slugField:
		retVal.append([slugField.name, getattr(instance, slugField.name)])
	else:
		for field in fields.fields:
			if isinstance(field, models.SlugField):
				retVal.append([field.name, getattr(instance, field.name)])

	return retVal



def slugifyDynamicly(sender, instance, signal, *args, **kwargs):
	"""
	for certain type of records we want to let slug field to be updated
	to reflect the current record value. In example for records (CrewMember,
	Skipper), slugfield is name only we want also to update url. so if someone
	makes typo such as jack lemmon (slug field jack_lemmon), and then fixes
	this to jake lemmon (slug field jake_lemmon), slug field gets updated.

	otherwise it behaves in the same way as the slugify.
	"""
	for slugs in _get_fields_and_data(instance):
		passedSlug = _string_to_slug(slugs[1])
		slug = passedSlug
		ct = 0;
		try:
			#look up object, if there is no such object,
			#exception is raised
			lookedUpInstance = sender.objects.get(pk=instance._get_pk_val())

			#find matching slug field
			slugMatch = False
			for originalSlug in _get_fields_and_data(lookedUpInstance):
				#slug field names and 'computed' values must match
				if _string_to_slug(originalSlug[1]) == slug and originalSlug[0] == slugs[0]:
					slugMatch = True
					break

			if slugMatch:
				print "slugMatch"
				slugList = _getSlugFieldVal(lookedUpInstance, originalSlug[0])
				slug = slugList[0][1]
			else:
				print "No Slug Match"
				slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator = "_")

			print slug
			print slugs[0].name
			setattr(instance, slugs[0].name, slug)

		except:
			slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator="_")
			setattr(instance, slugs[0].name, slug)

# ===========================
# To attach it to your model:
# ===========================
#
# dispatcher.connect(_package_.slugify, signal=signals.pre_save, sender=_your_model_)
